package lib.api;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;

import lib.XML.MyXmlRpcParser;
import redstone.xmlrpc.XmlRpcClient;
import redstone.xmlrpc.XmlRpcException;
import redstone.xmlrpc.XmlRpcFault;

/**
 * Created by IntelliJ IDEA.
 * User: lodder
 * Date: 20/08/11
 * Time: 13:22
 * To change this template use File | Settings | File Templates.
 */
public class XmlRPCAPI {

    private final String apiServer;
    private String userAgent;
    private String token;

    public XmlRPCAPI (String userAgent, String apiServer){
        this.apiServer = apiServer;
        this.userAgent = userAgent;
    }
    
    protected Map<?, ?> invoke(String method, Object... arguments)
            throws XmlRpcFault, MalformedURLException {
        try {
            XmlRpcClient rpc = new XmlRpcClient(getApiServer(), false);
            
            Map<?, ?> response = (Map<?, ?>) rpc.invoke(method, arguments);
            checkResponse(response);

            return response;
        } catch (XmlRpcFault e) {
            // invalidate session token if session has expired 406 is for opensubtitles 
        	// invalidate session token if session has expired 301, 302 is for opensubtitles 
            if (e.getErrorCode() == 406 || e.getErrorCode() == 301 || e.getErrorCode() == 302)
                token = null;

            // rethrow exception
            throw e;
        }
    }

    /**
     * Check whether status is OK or not
     * status code and message (e.g. 200 OK, 401 Unauthorized, ...)
     *
     * @param response
     * @throws XmlRpcFault thrown if status code is not OK
     */
    protected void checkResponse(Map<?, ?> response) throws XmlRpcFault {
        String status = response.get("status").toString();

        // if there is no status at all, assume everything was OK
        if (status == null || status.equals("200 OK") || status.equals("200")) {
            return;
        }
        

        try {
            throw new XmlRpcFault(new Scanner(status).nextInt(), status);
        } catch (NoSuchElementException e) {
            throw new XmlRpcException("Illegal status code: " + status);
        }
    }

    public String getApiServer() {
        return apiServer;
    }

    private InputStream sendRPC(String xml)
            throws IOException {
        URL url = new URL(getApiServer());
        URLConnection connection = url.openConnection();
        connection.setRequestProperty("Connection", "Close");
        connection.setRequestProperty("Content-Type", "text/xml");
        connection.setDoOutput(true);

        connection.getOutputStream().write(xml.getBytes("UTF-8"));

        return connection.getInputStream();
    }

    protected Map<?, ?> invokeXML(String xml) throws XmlRpcException,
            IOException, XmlRpcFault {
        MyXmlRpcParser parser = new MyXmlRpcParser();
        try {
            parser.parse(sendRPC(xml));
            Map<?, ?> response = (Map<?, ?>) parser.getReturnValue();
            checkResponse(response);

            return response;
        } catch (XmlRpcFault e) {
            // invalidate session token if session has expired
            if (e.getErrorCode() == 406 || e.getErrorCode() == 300)
                token = null;

            // rethrow exception
            throw e;
        }
    }

     public String generateXmlRpc(String procname, String s[]) {
        StringBuilder str = new StringBuilder();
        str.append("<?xml version=\"1.0\" encoding=\"utf-8\"?><methodCall><methodName>");
        str.append(procname).append("</methodName><params>");

        for (String value : s) {
            str.append("<param><value><string>").append(value).append("</string></value></param>");
        }

        str.append("</params></methodCall>");
        return str.toString();
    }

    public String generateXmlRpc(final String method,
                                 final Map<String, Object> arguments) {
        String str = "";
        str += "<?xml version=\"1.0\" encoding=\"utf-8\"?><methodCall><methodName>";
        str += method;
        str += "</methodName><params><param><value><string>";
        str += getToken();
        str += "</string></value></param><param><value><struct>";

        str += "<member><value><struct>";

        for (String s : arguments.keySet()) {
            str += addMapElement(s, arguments.get(s).toString());
        }

        str += "</struct></value></member>";

        str += "</struct></value></param></params></methodCall>";
        return str;
    }

    private static String addMapElement(final String name, final String value) {
        return "<member><name>" + name + "</name><value><string>"
                + elementEncoding(value) + "</string></value></member>";
    }

    private static String elementEncoding(String a) {
        a = a.replace("&", "&amp;");
        a = a.replace("<", "&lt;");
        a = a.replace(">", "&gt;");
        a = a.replace("'", "&apos;");
        a = a.replace("\"", "&quot;");
        return a;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }
}
